package com.beiding.my.common;

import com.beiding.my.common.query.Expression;
import com.beiding.my.common.query.InnerCompare;
import com.google.common.base.Joiner;
import org.checkerframework.checker.units.qual.A;
import org.hibernate.query.NativeQuery;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.Query;
import java.util.*;


//这里生成的是Bo指向的be
@Component
public class NativeQueryHelper {

    @Autowired
    private EntityManager entityManager;

    @Autowired
    private TypeTableMapperHelper typeTableMapperHelper;

    @Autowired
    private QuoteExpressionRegistry quoteExpressionRegistry;

    private  Query createQueryBo(String resultSet, String tableMapper, String condition, Map<String, Object> params, Class<? extends Bo> boType) {
        String sql = (String) Cache.get("NativeQueryHelper.createQueryBo", resultSet, tableMapper, condition, boType);
        if (sql == null) {
            synchronized (this) {
                if ((sql = (String) Cache.get("NativeQueryHelper.createQueryBo", resultSet, tableMapper, condition, boType)) == null) {
                    sql = "select " + resultSet + " from " + tableMapper;
                    if (condition != null) {
                        condition = condition.trim();
                        if (!condition.equals("")) {
                            sql += " where " + condition;
                        }
                    }
                    Cache.put(sql, "NativeQueryHelper.createQueryBo", resultSet, tableMapper, condition, boType);
                }
            }
        }
        Query query = entityManager.createNativeQuery(sql);
        query.unwrap(NativeQuery.class).setResultTransformer(Transformers.aliasToBean(BoVoBe.getBeTypeFromBoType(boType)));
        if (params != null && params.size() > 0) {
            for (Map.Entry<String, Object> entry : params.entrySet()) {
                query.setParameter(entry.getKey(), entry.getValue());
            }
        }
        return query;
    }

    public Object queryOneBo(String resultSet, String tableMapper, String condition, Map<String, Object> params, Class<? extends Bo> boType) {

        try {
            Query query = createQueryBo(resultSet, tableMapper, condition, params, boType);
            return query.getSingleResult();
        } catch (NoResultException e) {
            throw new RuntimeException(e);
        }
    }


    //TODO 加缓存
    public Object queryByHolder(Bo holder, Bo bo, String selfName) {
        if (holder.getId() == null) {
            return null;
        }
        Class<? extends Bo> aClass = bo.getClass();
        QuoteExpressionRegistry.QuoteExpression q =Cache.computeIfAbsent(() -> {
            QuoteExpressionRegistry.QuoteExpression quoteExpression = quoteExpressionRegistry.quoteCondition(holder.getClass(), aClass, selfName);
            Set<String> conditions = quoteExpression.getQuoteConditions();
            conditions.add(quoteExpression.getExpression());
            return quoteExpression;
        }, "NativeQueryHelper.queryByHolder", holder, bo, selfName);

        //
        String condition = Joiner.on(" and ").join(q.getQuoteConditions());

        Map<String, Object> params = new HashMap<>();
        params.put("holderId", holder.getId());

        return this.queryOneBo(this.returnSetStringBo(aClass), this.classTableMapperBe(q.getBeTypes()), condition, params, aClass);

    }


    public List queryListBo(String resultSet, String tableMapper, String condition, Map<String, Object> params, Class<? extends Bo> boType) {
        Query query = createQueryBo(resultSet, tableMapper, condition, params, boType);
        return query.getResultList();
    }

    public String classTableMapperBe(Class... types) {
        return classTableMapperBe(Arrays.asList(types));
    }

    public String classTableMapperBe(Iterable<Class> types) {
        String s = (String) Cache.get("NativeQueryHelper.classTableMapperBe", types);
        if (s == null) {
            synchronized (this) {
                if ((s = (String) Cache.get("NativeQueryHelper.classTableMapperBe", types)) == null) {
                    List<String> cts = new ArrayList<>();
                    for (Class clzz : types) {
                        TypeTableMapper mapper = typeTableMapperHelper.getTypeTableMapper(clzz);
                        cts.add(mapper.getTableName() + " " + mapper.getTableName());
                    }
                    s = Joiner.on(",").join(cts);
                    Cache.put(s, "NativeQueryHelper.classTableMapperBe", types);
                }
            }
        }
        return s;
    }


    //结果保留映射
    public String returnSetStringBo(Class<? extends Bo> type) {

        String r = (String) Cache.get("NativeQueryHelper.returnSetStringBo", type);
        if (r == null) {
            synchronized (this) {
                if ((r = (String) Cache.get("NativeQueryHelper.returnSetStringBo", type)) == null) {
                    TypeTableMapper typeTableMapper = typeTableMapperHelper.getTypeTableMapper(BoVoBe.getBeTypeFromBoType(type));
                    String tableName = typeTableMapper.getTableName();
                    List<String> strings = new ArrayList<>();
                    Map<String, String> differentFieldToTableColMap = typeTableMapper.getDifferentFieldToTableColMap();
                    for (String field : typeTableMapper.getFields()) {
                        String s = differentFieldToTableColMap.get(field);
                        if (s == null) {
                            strings.add(tableName + "." + field);
                        } else {
                            strings.add(tableName + "." + s + " as " + field);
                        }
                    }

                    r = Joiner.on(",").join(strings);
                    Cache.put(r, "NativeQueryHelper.returnSetStringBo", type);
                }
            }
        }
        return r;
    }

    public String returnSetStringIncludeBo(Class<? extends Bo> type, String... fs) {
        return returnSetStringIncludeBo(type, Arrays.asList(fs));
    }

    public String returnSetStringExcludeBo(Class<? extends Bo> type, String... fs) {
        return returnSetStringExcludeBo(type, Arrays.asList(fs));
    }


    public String returnSetStringExcludeBo(Class<? extends Bo> type, Collection<String> fs) {

        String r = (String) Cache.get("NativeQueryHelper.returnSetStringExcludeBo", type, fs);
        if (r == null) {
            synchronized (this) {
                if ((r = (String) Cache.get("NativeQueryHelper.returnSetStringExcludeBo", type, fs)) == null) {

                    TypeTableMapper typeTableMapper = typeTableMapperHelper.getTypeTableMapper(BoVoBe.getBeTypeFromBoType(type));
                    String tableName = typeTableMapper.getTableName();
                    List<String> strings = new ArrayList<>();
                    Map<String, String> differentFieldToTableColMap = typeTableMapper.getDifferentFieldToTableColMap();
                    for (String field : typeTableMapper.getFields()) {
                        if (fs.contains(field)) {
                            continue;
                        }
                        String s = differentFieldToTableColMap.get(field);
                        if (s == null) {
                            strings.add(tableName + "." + field);
                        } else {
                            strings.add(tableName + "." + s + " as " + field);
                        }
                    }
                    r= Joiner.on(",").join(strings);
                    Cache.put(r,"NativeQueryHelper.returnSetStringExcludeBo", type, fs);
                }

            }
        }

        return r;
    }

    public String returnSetStringIncludeBo(Class<? extends Bo> type, Collection<String> fs) {
        String r = (String) Cache.get("NativeQueryHelper.returnSetStringIncludeBo", type, fs);
        if (r == null) {
            synchronized (this) {
                if ((r = (String) Cache.get("NativeQueryHelper.returnSetStringIncludeBo", type, fs)) == null) {

                    TypeTableMapper typeTableMapper = typeTableMapperHelper.getTypeTableMapper(BoVoBe.getBeTypeFromBoType(type));
                    String tableName = typeTableMapper.getTableName();
                    List<String> strings = new ArrayList<>();
                    Map<String, String> differentFieldToTableColMap = typeTableMapper.getDifferentFieldToTableColMap();
                    for (String field : typeTableMapper.getFields()) {
                        if (!fs.contains(field)) {
                            continue;
                        }
                        String s = differentFieldToTableColMap.get(field);
                        if (s == null) {
                            strings.add(tableName + "." + field);
                        } else {
                            strings.add(tableName + "." + s + " as " + field);
                        }
                    }
                    r= Joiner.on(",").join(strings);
                    Cache.put(r,"NativeQueryHelper.returnSetStringExcludeBo", type, fs);
                }
            }
        }
        return r;
    }


    public Object querySelf(Bo bo, Expression exp) {
        Class<? extends Bo> aClass = bo.getClass();
        Expression.CreateResult expression = exp.createExpression(quoteExpressionRegistry, aClass);
        return this.queryOneBo(this.returnSetStringBo(aClass), this.classTableMapperBe(expression.getBeTypes()), expression.getExpression(), expression.getParams(), aClass);
    }

    public boolean exist(Class<? extends Bo> aClass, InnerCompare exp) {

        Expression.CreateResult expression = exp.createExpression(quoteExpressionRegistry, aClass);



        return false;
    }
}
